package com.ihr360.shared.sequence.service.impl;

import com.ihr360.commons.annotation.CodeGenerator;
import com.ihr360.commons.constant.Constants;
import com.ihr360.commons.context.session.Ihr360Session;
import com.ihr360.commons.context.session.Ihr360SessionContextHolder;
import com.ihr360.commons.exception.Ihr360Exception;
import com.ihr360.commons.lang.CollectionUtils;
import com.ihr360.commons.lang.DateUtils;
import com.ihr360.commons.lang.StringUtils;
import com.ihr360.expr.ExprException;
import com.ihr360.expr.ExprMissing;
import com.ihr360.expr.ExprString;
import com.ihr360.expr.core.Expr;
import com.ihr360.expr.core.ExprFunction;
import com.ihr360.expr.core.ExprVariable;
import com.ihr360.expr.engine.VariableFetcher;
import com.ihr360.shared.sequence.dao.Ihr360SequenceReposity;
import com.ihr360.shared.sequence.dao.Ihr360SequenceTemplateReposity;
import com.ihr360.shared.sequence.dto.Ihr360SequenceNo;
import com.ihr360.shared.sequence.dto.Ihr360SequenceTemplate;
import com.ihr360.shared.sequence.expression.SerialNumberEvaluation;
import com.ihr360.shared.sequence.service.CodeGeneratorService;
import org.apache.commons.beanutils.BeanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;

@Component
public class CodeGeneratorServiceImpl implements CodeGeneratorService {

    final Logger logger = LoggerFactory.getLogger(CodeGeneratorService.class.getName());

    @Autowired
    Ihr360SequenceReposity sequenceReposity;

    @Autowired
    Ihr360SequenceTemplateReposity sequenceTemplateReposity;


    @Transactional(rollbackFor = Exception.class, propagation = Propagation.MANDATORY)
    @Override
    public String generate(CodeGenerator codeGenerator, Object entity) {
        if (codeGenerator == null) {
            throw new Ihr360Exception(null, "生成器未定义");
        }
        String bizModel = codeGenerator.bizModel();
        String bizName = codeGenerator.name();
        String categoryId = codeGenerator.categoryId();

        if (StringUtils.isBlank(categoryId)) {
            categoryId = null;
        }
        Boolean isForCompany = codeGenerator.isForCompany();
        if (StringUtils.isBlank(codeGenerator.bizModel())) {
            throw new Ihr360Exception(null, "模块名称为空，无法生成序列号");
        }

        Ihr360SequenceTemplate template = null;
        String companyId = null;
        if (isForCompany) {
            Ihr360Session session = Ihr360SessionContextHolder.getSession();
            if (hasCompanyId(session)) {
                return null;
                //throw new Ihr360Exception(null, "无法获取会话信息");
            }
            companyId = session.getUserInfo().getCompanyId();
        } else {
            companyId = Constants.Company.SYSTEM;
        }

        template = sequenceTemplateReposity.findByBizModelAndCompanyId(bizModel, companyId);
        Long maxId = 1l;
        if (template == null) {//序列号还未初始化过，初始化
            template = getDefaultIhr360Sequence(codeGenerator, bizModel, bizName, companyId);
            template = sequenceTemplateReposity.save((Ihr360SequenceTemplate) template);
            maxId = template.getStartId();
        }


        SerialNumberEvaluation evaluation = null;

        if (StringUtils.isNotBlank(template.getCategoryId()) || StringUtils.isNotBlank(template.getTemplate())) {
            evaluation = new SerialNumberEvaluation(new VariableFetcher() {
                @Override
                public Expr variable(String variableId) {
                    try {
                        String arg = BeanUtils.getProperty(entity, variableId);
                        return new ExprString(arg);
                    } catch (Exception e) {
                        if (logger.isWarnEnabled()) {
                            logger.warn(e.getMessage(), e);
                        }
                        if (logger.isDebugEnabled()) {
                            logger.debug("Field {} not found in entity {}", variableId, entity.getClass().toString());
                        }
                    }
                    return null;
                }

                @Override
                public Expr notifyMissing(ExprVariable variable) {
                    return ExprMissing.MISSING;
                }

            });
        }
        categoryId = parseCategoryId(evaluation, template.getCategoryId());

        Ihr360SequenceNo sequenceNo = sequenceReposity.findByBizModelAndCompanyIdAndCategoryId(bizModel, companyId, categoryId);
        if (sequenceNo != null) {
            maxId = sequenceNo.getMaxId();
            sequenceNo.setMaxId(maxId + template.getStep());
        } else {
            maxId = template.getStartId();
            sequenceNo = new Ihr360SequenceNo();
            sequenceNo.setBizModel(template.getBizModel());
            sequenceNo.setMaxId(template.getStartId() + 1);
            sequenceNo.setTemplate(template);
            sequenceNo.setCompanyId(companyId);
            sequenceNo.setCategoryId(categoryId);
        }
        sequenceReposity.save(sequenceNo);
        return generateCode(evaluation, entity, template, maxId);
    }

    private boolean hasCompanyId(Ihr360Session session) {
        return session == null || session.getUserInfo() == null || StringUtils.isBlank(session.getUserInfo().getCompanyId());
    }


    private Ihr360SequenceTemplate getDefaultIhr360Sequence(CodeGenerator codeGenerator, String bizModel, String bizName, String comanyId) {
        Ihr360SequenceTemplate sequence;

        sequence = new Ihr360SequenceTemplate();

        sequence.setStartId(1l);
        sequence.setStep(1);
        sequence.setBizModel(bizModel);
        sequence.setBizModeDesc(bizName);
        sequence.setTemplate(codeGenerator.serialTmpl());
        sequence.setSerialLength(codeGenerator.length());
        sequence.setCategoryId(codeGenerator.categoryId());
        sequence.setCompanyId(comanyId);
        return sequence;
    }

    /**
     * 解析序列号分组中清零方式
     */
    private void parseCategoryCleanType(SerialNumberEvaluation evaluation, String categoryId) {
        //解析分组id设置
        if (StringUtils.isNotBlank(categoryId) && categoryId.indexOf('(') > 0 && categoryId.lastIndexOf(')') > 0) {
            String[] categorys = categoryId.substring(categoryId.indexOf('(') + 1, categoryId.lastIndexOf(')')).split(",");
            if (CollectionUtils.isNotEmpty(categorys)) {
                for (String cleanType : categorys) {
                    if (StringUtils.isNotBlank(cleanType) && SerialNumberEvaluation.categoryIdTypeList.contains(cleanType.toUpperCase())) {
                        evaluation.addVariable(cleanType.toUpperCase(), new ExprString(DateUtils.formatDateStr(new Date(), cleanType)));
                    }
                }
            }
        }
    }

    /**
     * 根据模板中对序列号分组标识的定义，生成唯一标识
     *
     * @param categoryId
     * @return
     */
    private String parseCategoryId(SerialNumberEvaluation evaluation, String categoryId) {
        if (StringUtils.isEmpty(categoryId)) {
            return null;
        }
        ExprFunction serialFunction = null;
        try {
            //解析分组id设置
            parseCategoryCleanType(evaluation, categoryId);

            serialFunction = (ExprFunction) evaluation.parse(categoryId);
            if (serialFunction == null) {
                throw new Ihr360Exception(null, "序列号生成器解析错误:" + categoryId);
            }
        } catch (Exception e) {
            //异常处理
            throw new Ihr360Exception(null, "序列号生成器解析错误:" + categoryId);
        }
        Expr result = null;
        try {
            result = serialFunction.evaluate(evaluation);
        } catch (ExprException e) {
            if (logger.isWarnEnabled()) {
                logger.warn(e.getMessage(), e);
            }
            throw new Ihr360Exception(null, "序列号生成失败,表达式异常");
        }
        if (result == null) {
            throw new Ihr360Exception(null, "序列号生成失败,表达式异常");
        }
        return result.toString();
    }

    /**
     * 解析序列号日期生成规则
     */
    private void parseTemplateDateType(String template, SerialNumberEvaluation evaluation) {
        //获取配置序列号日期格式
        if (StringUtils.isNotBlank(template) && template.indexOf('(') > 0 && template.lastIndexOf(')') > 0) {
            String[] settings = template.substring(template.indexOf('(') + 1, template.lastIndexOf(')')).split(",");
            //判断是否为流水号项目设置
            if (CollectionUtils.isNotEmpty(settings)) {
                for (String dateType : settings) {
                    if (StringUtils.isNotBlank(dateType) && SerialNumberEvaluation.dateTypeList.contains(dateType.toUpperCase())) {
                        evaluation.addVariable(dateType.toUpperCase(), new ExprString(DateUtils.formatDateStr(new Date(), dateType)));
                    }
                }
            }
        }
    }

    private String generateCode(SerialNumberEvaluation evaluation, Object entity, Ihr360SequenceTemplate sequenceTemplate, Long sequence) {
        String template = sequenceTemplate.getTemplate();
        String separator = sequenceTemplate.getSectionSeparator();
        int seqLength = sequenceTemplate.getSerialLength();
        if (sequence != null && sequence.toString().length() > seqLength) {
            throw new Ihr360Exception(null, sequenceTemplate.getBizModeDesc() + "生成失败，流水号长度超出设置长度，请重新设置。");
        }
        String serialNo = String.format("%0" + seqLength + "d", sequence);
        if (StringUtils.isBlank(template)) {
            return serialNo;
        }
        ExprFunction serialFunction = null;
        try {
            evaluation.addVariable(SerialNumberEvaluation.MAXID, new ExprString(serialNo));
            //获取配置序列号日期格式
            parseTemplateDateType(template, evaluation);
            serialFunction = (ExprFunction) evaluation.parse(template);
            if (serialFunction == null) {
                throw new Ihr360Exception(null, "序列号生成器解析错误:" + template);
            }
        } catch (Exception e) {
            //异常处理
            throw new Ihr360Exception(null, "序列号生成器解析错误:" + template);
        }
        Expr result = null;
        try {
            result = serialFunction.evaluate(evaluation);
        } catch (ExprException e) {
            if (logger.isWarnEnabled()) {
                logger.warn(e.getMessage(), e);
            }
            throw new Ihr360Exception(null, "序列号生成失败,表达式异常");
        }
        if (result == null) {
            throw new Ihr360Exception(null, "序列号生成失败,表达式异常");
        }
        return result.toString();
    }

}